#include "MeshInstance.h"

#include "Render/RenderAPI.h"

namespace SE
{
	void BezierSurface::onRender(const Matrix4f trannsform)
	{
		// Temporary fixed.
		static I32 precision = 25;
		I32 triangleCount = (precision - 1) * (precision - 1) * 2;

		RenderVertex3D* vertices = new RenderVertex3D[triangleCount * 3];
		Vector3f* points = new Vector3f[precision * precision];
		Vector3f* interpolatedControlPoints = new Vector3f[4];

		// Create all points.
		for (I32 i = 0; i < precision; ++i)
		{
			Float t1 = Float(i) / precision;
			for (I32 j = 0; j < 4; ++j)
			{
				interpolatedControlPoints[j] = calculateCubicBezierPoint(mControlPoints[j], t1);
			}
			for (I32 j = 0; j < precision; ++j)
			{
				Float t2 = Float(j) / precision;
				points[j + i * precision] = calculateCubicBezierPoint(interpolatedControlPoints, t2);
			}
		}

		// Create vertices.
		for (I32 i = 0; i < precision - 1; ++i)
		{
			for (I32 j = 0; j < precision - 1; ++j)
			{
				auto p0 = points[j + i * precision];
				auto p1 = points[j + 1 + i * precision];
				auto p2 = points[j + (i + 1) * precision];
				auto p3 = points[j + 1 + (i + 1) * precision];

				auto t0 = Vector2f(Float(j) / (precision - 1), Float(i) / (precision - 1));
				auto t1 = Vector2f(Float(j + 1) / (precision - 1), Float(i) / (precision - 1));
				auto t2 = Vector2f(Float(j) / (precision - 1), Float(i + 1) / (precision - 1));
				auto t3 = Vector2f(Float(j + 1) / (precision - 1), Float(i + 1) / (precision - 1));

				I32 vertexOffset = (j + i * (precision - 1)) * 6;

				RenderVertex3D v;
				v.color = Vector4f{ 1.0f };
				v.normal = glm::normalize(glm::cross(p1 - p0, p2 - p0));

				v.position = p0;
				v.texCoord = t0;
				vertices[vertexOffset + 0] = v;
				v.position = p2;
				v.texCoord = t2;
				vertices[vertexOffset + 1] = v;
				v.position = p1;
				v.texCoord = t1;
				vertices[vertexOffset + 2] = v;

				v.normal = glm::normalize(glm::cross(p3 - p1, p2 - p1));
				v.position = p2;
				v.texCoord = t2;
				vertices[vertexOffset + 3] = v;
				v.position = p3;
				v.texCoord = t3;
				vertices[vertexOffset + 4] = v;
				v.position = p1;
				v.texCoord = t1;
				vertices[vertexOffset + 5] = v;
			}
		}

		RenderAPI::SubmitTriangles(vertices, triangleCount, trannsform, nullptr);

		delete[] interpolatedControlPoints;
		delete[] points;
		delete[] vertices;
	}

	String BezierSurface::record()
	{
		StringStream ss;
		for (I32 i = 0; i < 4; ++i)
		{
			for (I32 j = 0; j < 4; ++j)
			{
				auto point = mControlPoints[i][j];
				ss << point.x << ' ' << point.y << ' ' << point.z;
				if (i != 3 && j != 3)
				{
					ss << ' ';
				}
			}
		}

		return ss.str();
	}

	Vector3f BezierSurface::calculateCubicBezierPoint(Vector3f* ps, const Float t)
	{
		Double u = 1.0f - t;
		Double tt = t * t;
		Double uu = u * u;
		Double uuu = uu * u;
		Double ttt = tt * t;
		Double uut = uu * t;
		Double ttu = tt * u;

		Vector3f point{ 0 };
		point.x = Float(uuu * ps[0].x + 3.0 * uut * ps[1].x + 3.0 * ttu * ps[2].x + ttt * ps[3].x);
		point.y = Float(uuu * ps[0].y + 3.0 * uut * ps[1].y + 3.0 * ttu * ps[2].y + ttt * ps[3].y);
		point.z = Float(uuu * ps[0].z + 3.0 * uut * ps[1].z + 3.0 * ttu * ps[2].z + ttt * ps[3].z);

		return point;
	}
} // !namespace SE